/*
 * TreeViewBean.java		Date created: Mar 3, 2008
 * Last modified by: $Author: vgolub $
 * $Revision: 333 $	$Date: 2008-03-21 15:53:56 +0200 (Fri, 21 Mar 2008) $
 */
package org.domain.tree.session;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.FacesEvent;
import javax.persistence.EntityManager;

import org.domain.tree.FormTreeNode;
import org.domain.tree.domain.Node;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.log.Log;
import org.richfaces.component.UITree;
import org.richfaces.component.UITreeNode;
import org.richfaces.component.html.HtmlTree;
import org.richfaces.event.NodeExpandedEvent;
import org.richfaces.event.NodeSelectedEvent;
import org.richfaces.model.TreeRowKey;


/**
 * Managed bean for representation of tree.
 * 
 * @author aareshchanka
 * 
 */
@Name("treeBean")
@Scope(ScopeType.SESSION)
@Transactional
public class TreeViewBean implements Serializable {

	/*
	 * Serialization constant.
	 */
	private static final long serialVersionUID = 2217000814051804282L;
	
	@In EntityManager entityManager;
	
	/*
	 * Log instance.
	 */
	@Logger
	private Log log;

	/* FormTreeNode instace for keeping data about tree */
	private FormTreeNode rootNode;

	/*
	 * {@link TreeViewBean} current selected node.
	 */
	@Out(scope = ScopeType.SESSION, required = false)
	private FormTreeNode selectedNode;


	/*
	 * {@link Node} instance for newly created nodes.
	 */
	private Node node_to_add = new Node();

	private boolean isTreeCollapseNeeded;



	/**
	 * method that populates rooteNode instances for representation of tree on
	 * the page
	 */
	public void populateTree() {
		List<Node> nodeList = getRoot();
		rootNode = new FormTreeNode();

		addNodes(rootNode, nodeList);
		
		selectedNode = rootNode;
	}
	
	/*
	 * Adds children for currentRootNode
	 *  
	 * @param currentRootNode rootNode that is populated with children
	 * @param childNodes nodes for population
	 */
	private void addNodes(FormTreeNode currentRootNode, List<Node> childNodes) {

		for (Node node : childNodes) {
			FormTreeNode tempNode = new FormTreeNode(node, currentRootNode);
			for (int i = 0; i < node.getCountChilds(); i++) {
				tempNode.addChildNode(new FormTreeNode());
			}
			currentRootNode.addChildNode(tempNode);
		}
	}



	/**
	 * Listener that performs on the tree expand action. Populates children of
	 * selected node
	 * 
	 * @param e NodeExpandedEvent from UI TreeNode component
	 */
	public void changeExpandListener(NodeExpandedEvent e) {
		setTreeCollapseNeeded(false);
		if (getTree(e).getTreeNode() != null) {
			FormTreeNode node = (FormTreeNode) (getTree(e).getTreeNode()).getData();
			if (node.getChildren().hasNext()
					&& ((FormTreeNode) ((Map.Entry) node.getChildren().next())
							.getValue()).getNode() == null) {
				node.getChildrenList().clear();
				addNodes(node, getChilds(
						node.getNode().getId()));
			}
			
//			 if (node.getNode().isOutdated()) {
//				changeStatus((FormTreeNode) node.getParent());
//			}
			 
		}
	}

	/**
	 * Listener that performs on the tree select action. Populates children of
	 * selected node.
	 * 
	 * @param e NodeExpandedEvent from UI TreeNode component
	 */
	public void nodeSelectionListener(NodeSelectedEvent e) {
		setTreeCollapseNeeded(false);
		
		if (getTree(e).getTreeNode() != null) {
			setNewNode((FormTreeNode) getTree(e).getTreeNode());
		}
	}
	
	public void setNewNode(FormTreeNode node) {
		selectedNode = node;

		if (selectedNode.getChildren().hasNext()
				&& ((FormTreeNode) ((Map.Entry) selectedNode.getChildren()
						.next()).getValue()).getNode() == null) {
			selectedNode.getChildrenList().clear();
			addNodes(selectedNode, getChilds(
					selectedNode.getNode().getId()));
		}
	}
	
	
	public Boolean adviseNodeSelected(UITree tree) {
		if (null != selectedNode && null != selectedNode.getNode()) {
			TreeRowKey treeRowKey = (TreeRowKey) tree.getRowKey();
			FormTreeNode treeNode = (FormTreeNode) tree.getRowData(treeRowKey);
			if (treeNode.equals(selectedNode)) {
				return Boolean.TRUE;
			}
		} else if (null != selectedNode && null == selectedNode.getNode()) {
			return Boolean.FALSE;
		}
		
		return null;
	}
	
	public Boolean adviseNodeOpened(UITree tree) {
		if (null != selectedNode && null != selectedNode.getNode() && null != selectedNode.getNode().getParentNode()) {
			TreeRowKey treeRowKey = (TreeRowKey) tree.getRowKey();
			FormTreeNode treeNode = (FormTreeNode) tree.getRowData(treeRowKey); 
			if (isSelectedNodeAncestor(treeNode)) {
				return Boolean.TRUE;
			}
		} else if (null != selectedNode && null == selectedNode.getNode()) {
			// Could be only if selectedNode equals to rootNode.
			if (isTreeCollapseNeeded) {
				return Boolean.FALSE;
			} 
		}
		
		return null;
	}
	
	private boolean isSelectedNodeAncestor(FormTreeNode treeNode) {
		Node selectedNodeAncestor = selectedNode.getNode().getParentNode();
		while (null != selectedNodeAncestor) {
			if (treeNode.getNode().equals(selectedNodeAncestor)) {
				return true;
			}
			
			selectedNodeAncestor = selectedNodeAncestor.getParentNode();
		}
		
		return false;
	}

	/**
	 * 
	 * @param event current JSF event
	 * @return instance of UITree ,depends on component type, otherwise null.
	 */
	private UITree getTree(FacesEvent event) {
		UIComponent component = event.getComponent();
		if (component instanceof UITree) {
			return ((UITree) component);
		}
		if (component instanceof UITreeNode) {
			return ((UITree) component.getParent());
		}
		return null;
	}

	/**
	 * @return the rootNode
	 */
	public FormTreeNode getRootNode() {
		if (rootNode == null) {
			populateTree();
		}
		return rootNode;
	}

	/**
	 * @param rootNode the rootNode to set
	 */
	public void setRootNode(FormTreeNode rootNode) {
		this.rootNode = rootNode;
	}

	/**
	 * @return the node_to_add
	 */
	public Node getNode_to_add() {
		return node_to_add;
	}

	/**
	 * @param node_to_add the node_to_add to set
	 */
	public void setNode_to_add(Node node_to_add) {
		this.node_to_add = node_to_add;
	}


	/**
	 * @param reportContentRendered the reportContentRendered to set
	 */
	public void setReportContentRendered(boolean reportContentRendered) {
		// Stub for jsf
	}

	/**
	 * @return true if selectedNode is not null and is leaf, otherwise false
	 */
	public boolean isReportContentRendered() {
		return null != getSelectedNode() && getSelectedNode().isLeaf();
	}

	/**
	 * @return the selectedNode
	 */
	public FormTreeNode getSelectedNode() {
		return selectedNode;
	}

	/**
	 * @param selectedNode the selectedNode to set
	 */
	public void setSelectedNode(FormTreeNode selectedNode) {
		this.selectedNode = selectedNode;
	}

	public void setTreeCollapseNeeded(boolean isTreeCollapseNeeded) {
		this.isTreeCollapseNeeded = isTreeCollapseNeeded;
	}

	public boolean isTreeCollapseNeeded() {
		return isTreeCollapseNeeded;
	}
	
	/**
	 * Get root of hierarchy of nodes for populating first tier of tree
	 * 
	 * @return List of nodes, hasn't parent
	 */
	@SuppressWarnings("unchecked")
	public List<Node> getRoot() {
		List<Node> root = entityManager.createQuery(
				"select n from Node n where n.parentNode = null")
				.getResultList();
		return root;
	}
	
	/**
	 * Get childs of node for populating next tier of tree
	 * 
	 * @param id -
	 *            id to lookup
	 * @return List of childs for specified node
	 */
	@SuppressWarnings("unchecked")
	public List<Node> getChilds(Long id) {
		List<Node> childs = entityManager.createQuery(
				"select n.childNodes from Node n where n.id =:id ")
				.setParameter("id", id).getResultList();
		return childs;
	}

}
